using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using T.FAS.Runtime.Base.Entity;
using T.FAS.Runtime.DataAccess.Common;
using T.FAS.Runtime.DataAccess.Kernel;
using T.FAS.Runtime.DataAccess.SPI;
using T.FAS.Runtime.DataAccess.SQLDom;
using T.FAS.Runtime.Metadata.Entity.DataModel;
using T.ServiceComponent.Kernel.Service;

namespace T.FAS.Runtime.DataAccess.Engine.Service
{
    class CDODataDeleteService : IDeleteService<CompositeDataObject>
    {
        public event ExecuteDataAccessAction Execute;
        public event BuildDataAccessActionContext BuildContext;
        private readonly IDatabase _db = DatabaseFactory.CreateDefaultDatabase();
        private readonly IDACLogService _localLogService = DACLogFactory.GetDACLogService();
        private readonly OpeType _opeType = OpeType.Delete;

        /// <summary>
        /// 删除指定的数据
        /// </summary>
        /// <remarks>按主键数据作为删除依据</remarks>
        /// <param name="metadata">领域模型</param>        
        /// <param name="dataID">主键数据</param>        
        /// <returns>删除SQL</returns>
        public void DeleteByID(CompositeDataObject metadata, string dataID, Dictionary<string, object> exrParams = null)
        {
            RpcContext.Current.Increase();
            if (metadata == null)
                throw new FasException(DataAccessErrorCode.CompositeDataObject.NullValue, CommonStringClassEngine.CDOService_CDOCannotBeNull);
            if (string.IsNullOrWhiteSpace(dataID))
                throw new FasException(DataAccessErrorCode.CompositeDataObject.NullValue, CommonStringClassEngine.DataModelService_DataIDCannotBeNull);
            DateTime start = DateTime.Now;
            Exception ex = null;
            try
            {
                var sqls = SQLBuilderFactory.CreateCDOSQLBuilder().ParseDeleteSqlByID(metadata, dataID);
                //FAS低代码改动：注册SQL成功执行后事件
                //InitDBEvent();
                RegistSQLExeSuccessdEvent(metadata);

                //初始化插件
                PluginInit(metadata, dataID, null, null, exrParams);

                //触发入口事件
                PluginExecute(InjectionPoint.Entry);

                PluginExecute(InjectionPoint.BeforeExecuteSQL);

                ExecuteSQL(metadata, sqls);

                PluginExecute(InjectionPoint.Finish);
            }
            catch (Exception e)
            {
                _localLogService.WriteError(e, $"CDO:{metadata.Code}执行Delete异常，msg:{e.Message}");
                ex = e;
                CDOMonitor.RootCaseReport(metadata, start, ex, _opeType);
            }
            finally
            {
                CDOMonitor.MonitorTPS(metadata, _opeType, ex);
                CDOMonitor.MonitorTrace(metadata, start, _opeType, ex, null, dataID);
                CDOMonitor.MonitorDuration(metadata, (DateTime.Now - start).TotalMilliseconds, _opeType, ex);
            }
        }

        /// <summary>
        /// 批量删除指定的数据
        /// </summary>
        /// <remarks>按主键数据作为删除依据</remarks>
        /// <param name="metadata">领域模型</param>        
        /// <param name="dataID">主键数据</param>        
        /// <returns>删除SQL</returns>
        public void DeleteByIDList(CompositeDataObject metadata, IEnumerable<string> dataIDs, Dictionary<string, object> exrParams = null)
        {
            RpcContext.Current.Increase();
            if (metadata == null)
                throw new FasException(DataAccessErrorCode.CompositeDataObject.NullValue, CommonStringClassEngine.CDOService_CDOCannotBeNull);
            if (dataIDs == null || dataIDs.Count() == 0)
                throw new FasException(DataAccessErrorCode.CompositeDataObject.NullValue, CommonStringClassEngine.DataModelService_DataIDCannotBeNull);
            DateTime start = DateTime.Now;
            Exception ex = null;

            try
            {
                var sqlList = new SqlStatementCollection();
                foreach (var dataID in dataIDs)
                {
                    var sqls = SQLBuilderFactory.CreateCDOSQLBuilder().ParseDeleteSqlByID(metadata, dataID);
                    sqlList.AddRange(sqls);
                }
                //FAS低代码改动：注册SQL成功执行后事件
                //InitDBEvent();
                RegistSQLExeSuccessdEvent(metadata);

                //初始化插件
                PluginInit(metadata, null, dataIDs.ToList(), null, exrParams);

                //触发入口事件
                PluginExecute(InjectionPoint.Entry);

                PluginExecute(InjectionPoint.BeforeExecuteSQL);

                ExecuteSQL(metadata, sqlList);

                PluginExecute(InjectionPoint.Finish);

            }
            catch (Exception e)
            {
                _localLogService.WriteError(e, $"CDO:{metadata.Code}执行Delete异常，msg:{e.Message}");
                ex = e;
                CDOMonitor.RootCaseReport(metadata, start, ex, _opeType);
            }
            finally
            {
                CDOMonitor.MonitorTPS(metadata, _opeType, ex);
                CDOMonitor.MonitorTrace(metadata, start, _opeType, ex, null, string.Join(",", dataIDs));
                CDOMonitor.MonitorDuration(metadata, (DateTime.Now - start).TotalMilliseconds, _opeType, ex);
            }
        }

        /// <summary>
        /// 删除指定的数据
        /// </summary>
        /// <remarks>按主键数据作为删除依据</remarks>
        /// <param name="metadata">领域模型</param> 
        /// <param name="cnoId">领域对象</param>
        /// <param name="dataID">主键数据</param>           
        public void DeleteByID(CompositeDataObject metadata, string cnoId, string dataID, Dictionary<string, object> exrParams = null)
        {
            if (metadata == null)
                throw new FasException(DataAccessErrorCode.CompositeDataObject.NullValue, CommonStringClassEngine.CDOService_CDOCannotBeNull);
            if (string.IsNullOrWhiteSpace(cnoId))
                throw new FasException(DataAccessErrorCode.CompositeDataObject.NullValue, CommonStringClassEngine.DataModelService_CNOIDCannotBeNull);
            if (string.IsNullOrWhiteSpace(dataID))
                throw new FasException(DataAccessErrorCode.CompositeDataObject.NullValue, CommonStringClassEngine.DataModelService_DataIDCannotBeNull);

            var cno = metadata.CompositeNodeObjects.FirstOrDefault(i => i.ID == cnoId);
            if (cno == null)
            {
                throw new FasException(DataAccessErrorCode.CompositeDataObject.NullValue, CommonStringClassEngine.CDOService_CannotFindCNOInstance+ cnoId);
            }

            var sqls = SQLBuilderFactory.CreateCDOSQLBuilder().ParseDeleteSqlByID(metadata, cno, dataID);
            //FAS低代码改动：注册SQL成功执行后事件
            //InitDBEvent();
            RegistSQLExeSuccessdEvent(metadata);

            //初始化插件
            PluginInit(metadata, dataID, null, cnoId, exrParams);

            //触发入口事件
            PluginExecute(InjectionPoint.Entry);

            PluginExecute(InjectionPoint.BeforeExecuteSQL);

            ExecuteSQL(metadata, sqls);

            PluginExecute(InjectionPoint.Finish);
        }

        /// <summary>
        /// 批量删除指定的数据
        /// </summary>
        /// <remarks>按主键数据作为删除依据</remarks>
        /// <param name="metadata">领域模型</param> 
        /// <param name="cnoId">领域对象</param>
        /// <param name="dataID">主键数据</param>                
        public void DeleteByIDList(CompositeDataObject metadata, string cnoId, IEnumerable<string> dataIDs, Dictionary<string, object> exrParams = null)
        {
            if (metadata == null)
                throw new FasException(DataAccessErrorCode.CompositeDataObject.NullValue, CommonStringClassEngine.CDOService_CDOCannotBeNull);
            if (string.IsNullOrWhiteSpace(cnoId))
                throw new FasException(DataAccessErrorCode.CompositeDataObject.NullValue, CommonStringClassEngine.DataModelService_CNOIDCannotBeNull);
            if (dataIDs == null || dataIDs.Count() == 0)
                throw new FasException(DataAccessErrorCode.CompositeDataObject.NullValue, CommonStringClassEngine.DataModelService_DataIDCannotBeNull);

            var cno = metadata.CompositeNodeObjects.FirstOrDefault(i => i.ID == cnoId);
            if (cno == null)
            {
                throw new FasException(DataAccessErrorCode.CompositeDataObject.NullValue, CommonStringClassEngine.CDOService_CannotFindCNOInstance + cnoId);
            }

            var sqlList = new SqlStatementCollection();
            foreach (var dataID in dataIDs)
            {
                var sqls = SQLBuilderFactory.CreateCDOSQLBuilder().ParseDeleteSqlByID(metadata, cno, dataID);
                sqlList.AddRange(sqls);
            }
            //FAS低代码改动：注册SQL成功执行后事件
            //InitDBEvent();
            RegistSQLExeSuccessdEvent(metadata);

            //初始化插件
            PluginInit(metadata, null, dataIDs.ToList(), null, exrParams);

            //触发入口事件
            PluginExecute(InjectionPoint.Entry);

            PluginExecute(InjectionPoint.BeforeExecuteSQL);

            ExecuteSQL(metadata, sqlList);

            PluginExecute(InjectionPoint.Finish);
        }

        #region CDO插件执行相关

        private void InitDBEvent()
        {
            //db.BeforeSQLExecute += () => this.Execute(InjectionMethodType.Delete, InjectionPoint.BeforeExecuteSQL);
            //db.ExecuteSQLSuccessed += result => this.Execute(InjectionMethodType.Delete, InjectionPoint.ExecuteSQLSuccess);
        }

        private void RegistSQLExeSuccessdEvent(CompositeDataObject compositeDataObject)
        {
            var rootNode = compositeDataObject.RootCompositeNodeObject;
            _db.ExecuteSQLSuccessed += (result) =>
            {
                if (result.SQLExecuteContext.SqlBuildingInfo.CurrentNode.ID != rootNode.ID)  //只判断主节点数据影响行数
                    return;
                if(result.AffectedRows == 0)
                {
                    StringBuilder sb = new StringBuilder();
                    var paras = result.SQLExecuteContext.Parameters;
                    if (paras != null && paras.Count > 0)
                    {
                        foreach (var para in paras)
                        {
                            if (para == null)
                                continue;
                            sb.AppendLine($"ParaName:{para.ParameterName}, ParaValue:{para.Value?.ToString()}");
                        }
                    }
                    throw new FasException(DataAccessErrorCode.CompositeDataObject.AffectedRowsError, string.Format(CommonStringClassEngine.CDODataDeleteService_RegistSQLExeSuccessdEvent,compositeDataObject?.Code));
                }
            };
        }

        public void PluginInit(CompositeDataObject compositeDataObject, string Id, List<string> IDs, string cnoId = null, Dictionary<string, object> exrParams = null)
        {
            var invokeContext = DataAccessPluginExecutor.BuildInvokeContext(DataModelConst.CDOMetaType, compositeDataObject, cnoId, null, Id, IDs);
            this.BuildContext(invokeContext);
        }

        private void PluginExecute(InjectionPoint injectionPoint)
        {
            /************统一数据访问插件执行点 Start************/
            try
            {
                //var invokeContext = DataAccessPluginExecutor.BuildInvokeContext(DataModelConst.CDOMetaType, compositeDataObject, cnoId, null, Id, IDs);
                //this.BuildContext(invokeContext);
                var actionResult = this.Execute(InjectionMethodType.Delete, injectionPoint);
                DataAccessPluginExecutor.DefaultHandleActionResult(actionResult);
            }
            catch (Exception e)
            {
                throw new FasException(DataAccessErrorCode.PlugIn.Error, string.Format(CommonStringClassEngine.DataModelService_PluginExecFail,e.Message), e);
            }
        }

        #endregion

        #region Private Funcs
        /// <summary>
        /// 统一执行SQL，提供友好性报错提示
        /// </summary>
        /// <param name="metaData"></param>
        /// <param name="sqls"></param>
        /// <returns></returns>
        private void ExecuteSQL(CompositeDataObject metaData, SqlStatementCollection sqls)
        {
            try
            {
                _db.ExecuteSQLWithTransaction(sqls);
            }
            catch (Exception e)
            {
                throw new FasException(DataAccessErrorCode.CompositeDataObject.Error, string.Format(CommonStringClassEngine.CDODataDeleteService_ExecuteSQL,metaData?.Code), e);
            }
        }
        #endregion
    }
}
